package preprocessor.absorption;

import java.util.HashMap;
import java.util.Map;

import utils.exception.AbsorptionException;

//import entities.InclusionAxiom;
import entities.concept.AndConcept;
import entities.concept.AtomicConcept;
import entities.concept.Concept;
import entities.concept.Nominal;

public class BinaryAbsorptionGuarded implements Absorption {
	private Map<AtomicConcept, Map<AtomicConcept, Concept>> specialBinaryEntries = new HashMap<AtomicConcept, Map<AtomicConcept, Concept>>();
	private long numEntry = 0;
	
	


	public BinaryAbsorptionGuarded() {
	}

	public void clearEntries() {
		specialBinaryEntries.clear();
	}

	public long getNumberOfEntries() {
		return numEntry;
	}

	public Map<AtomicConcept, Map<AtomicConcept, Concept>> getTbLimited() {
		return specialBinaryEntries;
	}

	public boolean absorb() {
		return false;
	}

	public static enum entryOrder {
		Nominal_First, Nominal_Second
	}

	public static entryOrder defOrder = entryOrder.Nominal_Second;

	// entryorder type can be null, default to "nominal second place"
	public void addBinaryEntry(AtomicConcept lk, AtomicConcept rk, Concept v) throws AbsorptionException {
		Nominal o = null;
		AtomicConcept ac = null;
		if (lk instanceof Nominal) {
			o = (Nominal) lk;
			ac = rk;
		} else if (rk instanceof Nominal) {
			o = (Nominal) rk;
			ac = lk;
		} else if (Absorption.BA_Limited) {
			// only special binary absorption allowed
			throw new AbsorptionException(
					"Only limited binary absorption is allowed. "
							+ "Must have *one nominal* in the keys.");
		}
		if (o == null) {
			addEntry(lk, rk, v);
			return;
		}
		// if(o.getName().contains("canon_eos10d_1")){
		// System.out.println(lk+" + "+rk+" = "+v);
		// }
		if (defOrder.equals(entryOrder.Nominal_First)) {
			addEntry(o, ac, v);
			return;
		} else if (defOrder.equals(entryOrder.Nominal_Second)) {
			addEntry(ac, o, v);
			return;
		} else {
			addEntry(lk, rk, v);
			return;
		}
	}

	// order preserving: lk AND rk ---> v
	private void addEntry(AtomicConcept lk, AtomicConcept rk, Concept v) {
		numEntry++;
		if (v.isTop()) {
			// ignore top
			return;
		}
		Map<AtomicConcept, Concept> im = null;
		if (specialBinaryEntries.containsKey(lk)) {
			im = specialBinaryEntries.get(lk);
		} else {
			im = new HashMap<AtomicConcept, Concept>();
		}
		Concept value = null;
		if (im.containsKey(rk)) {
			value = new AndConcept(v, im.get(rk));
		} else {
			value = v;
		}
		im.put(rk, value);
		specialBinaryEntries.put(lk, im);
	}

	public void updateEntryValue(AtomicConcept lk, AtomicConcept rk, Concept v
			) throws AbsorptionException {
		Nominal o = null;
		AtomicConcept ac = null;
		if (lk instanceof Nominal) {
			o = (Nominal) lk;
			ac = rk;
		} else if (rk instanceof Nominal) {
			o = (Nominal) rk;
			ac = lk;
		} else if (Absorption.BA_Limited) {
			// only special binary absorption allowed
			throw new AbsorptionException(
					"Only limited binary absorption is allowed. "
							+ "Must have *one nominal* in the keys.");
		}
		if (o == null) {
			return;
		}
		if (defOrder.equals(entryOrder.Nominal_First)) {
			updateEntryValuePrivate(o, ac, v);
			return;
		} else if (defOrder.equals(entryOrder.Nominal_Second)) {
			updateEntryValuePrivate(ac, o, v);
			return;
		} else {
			updateEntryValuePrivate(lk, rk, v);
			return;
		}
	}
	
	// order preserving: lk AND rk ---> v, use the original order
	private void updateEntryValuePrivate(AtomicConcept lk, AtomicConcept rk, Concept v) {
		Map<AtomicConcept, Concept> im = null;
		if (specialBinaryEntries.containsKey(lk)) {
			im = specialBinaryEntries.get(lk);
		} else {
			return;
		}
		if (im.containsKey(rk)) {
			im.put(rk, v);
		} else {
			return;
		}
		specialBinaryEntries.put(lk, im);
	}
	
	
	public Concept getEntry(AtomicConcept lk, AtomicConcept rk) {
		Nominal o = null;
		AtomicConcept ac = null;
		if (lk instanceof Nominal) {
			o = (Nominal) lk;
			ac = rk;
		} else if (rk instanceof Nominal) {
			o = (Nominal) rk;
			ac = lk;
		} else {
			return null;
		}
		if (defOrder.equals(entryOrder.Nominal_First)) {
			if (specialBinaryEntries.containsKey(o)) {
				Map<AtomicConcept, Concept> im = specialBinaryEntries.get(o);
				if (im.containsKey(ac)) {
					return im.get(ac);
				}
			}
			return null;
		}
		if (defOrder.equals(entryOrder.Nominal_Second)) {
			if (specialBinaryEntries.containsKey(ac)) {
				Map<AtomicConcept, Concept> im = specialBinaryEntries.get(ac);
				if (im.containsKey(o)
				// || im.keySet().contains(o)
				) {
					return im.get(o);
				}
			}
			return null;
		}
		return null;
	}

}
